<template>
	<!-- 
    根容器，负责捕获触摸事件。
    overflow-hidden 是必须的，以防止内部元素溢出导致页面滚动。
  -->
	<div
		class="flex-1 overflow-hidden h-full"
		@touchstart="handleTouchStart"
		@touchmove="handleTouchMove"
		@touchend="handleTouchEnd"
	>
		<!-- 
      可变换的内部视图，用于实现下拉和回弹动画。
      它的 transform 由 computedStyle 动态控制。
    -->
		<div class="flex flex-col h-full scrollbar-hide" :style="computedStyle">
			<!-- 
        下拉刷新提示区域。
        使用 slot 允许父组件自定义，同时提供了默认实现。
      -->
			<div
				ref="pullDownTipRef"
				class="flex-shrink-0 flex justify-center items-center h-16 text-gray-500"
				:style="{ opacity: pullDownOpacity }"
			>
				<slot
					name="header"
					:is-refreshing="isRefreshing"
					:pull-down-text="pullDownText"
				>
					<!-- 默认 Header 内容 -->
					<svg
						v-if="isRefreshing"
						class="animate-spin h-5 w-5 mr-2"
						xmlns="http://www.w3.org/2000/svg"
						fill="none"
						viewBox="0 0 24 24"
					>
						<circle
							class="opacity-25"
							cx="12"
							cy="12"
							r="10"
							stroke="currentColor"
							stroke-width="4"
						></circle>
						<path
							class="opacity-75"
							fill="currentColor"
							d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
						></path>
					</svg>
					<span>{{ pullDownText }}</span>
				</slot>
			</div>

			<!-- 
        主滚动容器。
        - `scrollRootId` 用于 IntersectionObserver 定位。
        - `min-h-0` 是 flex 布局下让 overflow-y 生效的关键。
      -->
			<div
				ref="scrollContainerRef"
				:id="scrollRootId"
				class="flex-1 overflow-y-auto scrollbar-hide min-h-0"
				:style="{ 'touch-action': touchAction }"
			>
				<!-- 列表内容 接收内部class -->
				<div v-if="dataList.length > 0" :class="contentClass">
					<div
						v-for="(item, index) in dataList"
						:key="item.id || index"
					>
						<!-- 
              #item 插槽，将数据和索引传回给父组件进行渲染。
              这是组件最核心的定制点。
            -->
						<slot name="item" :item="item" :index="index"></slot>
					</div>
				</div>

				<!-- 空状态 -->
				<div v-else-if="!isLoading">
					<slot name="empty">
						<div
							class="text-center p-12 text-gray-500 dark:text-gray-400"
						>
							暂无数据
						</div>
					</slot>
				</div>

				<!-- 底部加载状态指示器 -->
				<div class="text-center p-8 text-gray-500 dark:text-gray-400">
					<slot
						name="footer"
						:is-loading="isLoading"
						:is-finished="isFinished"
					>
						<!-- 默认 Footer 内容 -->
						<p v-if="isLoading">正在加载中...</p>
						<p v-if="isFinished && dataList.length > 0">
							已经到底啦~
						</p>
					</slot>
					<!-- 哨兵元素，用于触发上拉加载 -->
					<div ref="sentinelRef" class="h-1"></div>
				</div>
			</div>
		</div>
	</div>
</template>

<script setup lang="ts">
import {
	ref,
	computed,
	onMounted,
	onUnmounted,
	watch,
	nextTick,
	type PropType,
} from "vue";

// --- Props ---
const props = defineProps({
	/**
	 * 列表的数据源
	 */
	dataList: {
		type: Array as PropType<any[]>,
		required: true,
	},
	/**
	 * 是否启用下拉刷新功能
	 */
	pullDownRefreshEnabled: {
		type: Boolean,
		default: true,
	},
	/**
	 * 是否启用上拉加载更多功能
	 */
	loadMoreEnabled: {
		type: Boolean,
		default: true,
	},
	/**
	 * 是否启用左右滑动切换功能
	 */
	swipeEnabled: {
		type: Boolean,
		default: true,
	},
	/**
	 * 当前是否处于加载中状态 (由父组件控制)
	 */
	isLoading: {
		type: Boolean,
		default: false,
	},
	/**
	 * 是否所有数据都已加载完毕 (由父组件控制)
	 */
	isFinished: {
		type: Boolean,
		default: false,
	},
	/**
	 * 用于 IntersectionObserver 的滚动根元素 ID。
	 * 确保这个 ID 在页面中是唯一的。
	 */
	scrollRootId: {
		type: String,
		default: "list-view-scroll-container",
	},
  /**
   * 应用于列表内容包装器的 CSS 类
   */
  contentClass: {
    type: String,
    default: ''
  }
});

// --- Emits ---
const emit = defineEmits<{
	(e: "pull-down-refresh"): void;
	(e: "load-more"): void;
	(e: "swipe-left"): void;
	(e: "swipe-right"): void;
}>();

// --- Refs ---
const scrollContainerRef = ref<HTMLElement | null>(null);
const pullDownTipRef = ref<HTMLElement | null>(null);
const sentinelRef = ref<HTMLElement | null>(null);

// --- 状态管理 ---
const isTouching = ref(false);
const isDragging = ref(false);
const isRefreshing = ref(false);
const dragDirection = ref<"horizontal" | "vertical" | null>(null);

// --- 坐标与位移 ---
const touchStartX = ref(0);
const touchStartY = ref(0);
const deltaX = ref(0);
const deltaY = ref(0);

// --- 常量 ---
const ANIMATION_DURATION = 300;
const PULL_DOWN_THRESHOLD = 80;
const SWIPE_THRESHOLD = 50;

// --- 下拉刷新相关 ---
const pullDownTipHeight = ref(64); // 默认值，onMounted 时会更新
const pullDownOpacity = ref(0);
const pullDownText = ref("下拉刷新");

// --- 样式计算 ---
const computedStyle = computed(() => {
	const pullDownY =
		dragDirection.value === "vertical"
			? Math.max(0, deltaY.value - pullDownTipHeight.value)
			: 0;
	return {
		transform: `translate3d(${deltaX.value}px, ${pullDownY}px, 0)`,
		transition: !isTouching.value
			? `transform ${ANIMATION_DURATION}ms ease-out`
			: "none",
		marginTop: `-${pullDownTipHeight.value}px`,
		height: `calc(100% + ${pullDownTipHeight.value}px)`,
	};
});

// 解决刷新时的冲突报错
const touchAction = computed(() => {
	// 如果正在进行垂直拖拽（即下拉刷新操作）
	if (props.pullDownRefreshEnabled && dragDirection.value === 'vertical' && deltaY.value > 0) {
			// 禁止浏览器处理任何触摸操作，将控制权完全交给JS
			return 'none';
	}
	// 否则，允许浏览器处理垂直方向的平移（即原生滚动）
	return 'pan-y';
});

/**
 * 重置所有与拖拽手势相关的状态。
 * 这个函数是解决状态残留问题的关键。
 */
// const resetDragState = () => {
//   isTouching.value = false;
//   isDragging.value = false;
//   dragDirection.value = null;
//   deltaX.value = 0;
//   deltaY.value = 0;
//   pullDownOpacity.value = 0;
//   pullDownText.value = "下拉刷新";
// };


// --- 触摸事件处理 ---
const handleTouchStart = (e: TouchEvent) => {
	if (isRefreshing.value) return;

	isTouching.value = true;
	isDragging.value = false;
	dragDirection.value = null;
	deltaX.value = 0;
	deltaY.value = 0;
	touchStartX.value = e.changedTouches[0].clientX;
	touchStartY.value = e.changedTouches[0].clientY;
	pullDownText.value = "下拉刷新";
};

const handleTouchMove = (e: TouchEvent) => {
	if (!isTouching.value || isRefreshing.value) return;

	const currentDeltaX = e.changedTouches[0].clientX - touchStartX.value;
	const currentDeltaY = e.changedTouches[0].clientY - touchStartY.value;

	if (!isDragging.value) {
		if (Math.abs(currentDeltaX) > 10 || Math.abs(currentDeltaY) > 10) {
			isDragging.value = true;
			dragDirection.value =
				Math.abs(currentDeltaX) > Math.abs(currentDeltaY)
					? "horizontal"
					: "vertical";
		}
	}

	if (isDragging.value) {
		// 检查事件是否仍然可以被取消。如果不能，就不要阻止默认行为。
		// 这是一个额外的保险措施，配合 touch-action 使用。
		if (!e.cancelable) return;
		if (props.swipeEnabled && dragDirection.value === "horizontal") {
			e.preventDefault();
			deltaX.value = currentDeltaX;
		} else if (
			props.pullDownRefreshEnabled &&
			dragDirection.value === "vertical" &&
			scrollContainerRef.value?.scrollTop === 0 &&
			currentDeltaY > 0
		) {
			e.preventDefault();
			deltaY.value = currentDeltaY;
			pullDownOpacity.value = Math.min(
				1,
				deltaY.value / PULL_DOWN_THRESHOLD
			);
			pullDownText.value =
				deltaY.value > PULL_DOWN_THRESHOLD ? "松开刷新" : "下拉刷新";
		}
	}
};

const handleTouchEnd = () => {
	if (!isTouching.value) return;

	isTouching.value = false;

	if (
		props.swipeEnabled &&
		dragDirection.value === "horizontal" &&
		Math.abs(deltaX.value) > SWIPE_THRESHOLD
	) {
		if (deltaX.value < 0) {
			emit("swipe-left");
		} else {
			emit("swipe-right");
		}
	} else if (
		props.pullDownRefreshEnabled &&
		dragDirection.value === "vertical" &&
		deltaY.value > PULL_DOWN_THRESHOLD
	) {
		performPullDownRefresh();
	}

	// 无论触发了什么操作，都重置位移，播放回弹动画
	deltaX.value = 0;
	deltaY.value = 0;
	pullDownOpacity.value = 0;

	setTimeout(() => {
		isDragging.value = false;
	}, ANIMATION_DURATION);
};

// --- 功能函数 ---
const performPullDownRefresh = async () => {
	isRefreshing.value = true;
	pullDownText.value = "正在刷新...";
	deltaY.value = PULL_DOWN_THRESHOLD; // 固定在触发位置

	emit("pull-down-refresh");
};

// 监听 isLoading 的变化，当父组件数据加载完成后，收起刷新提示
watch(
	() => props.isLoading,
	(newVal, oldVal) => {
		if (oldVal === true && newVal === false && isRefreshing.value) {
			deltaY.value = 0;
			isRefreshing.value = false;
		}
	}
);

// --- 上拉加载 IntersectionObserver ---
let observer: IntersectionObserver | null = null;

const setupIntersectionObserver = () => {
	if (!props.loadMoreEnabled || !scrollContainerRef.value) return;

	observer = new IntersectionObserver(
		([entry]) => {
			if (
				entry &&
				entry.isIntersecting &&
				!props.isLoading &&
				!props.isFinished
			) {
				emit("load-more");
			}
		},
		{
			root: scrollContainerRef.value,
			threshold: 0.1,
		}
	);

	if (sentinelRef.value) {
		observer.observe(sentinelRef.value);
	}
};

// --- 生命周期钩子 ---
onMounted(() => {
	if (pullDownTipRef.value) {
		pullDownTipHeight.value = pullDownTipRef.value.clientHeight;
	}
	nextTick(() => {
		setupIntersectionObserver();
	});
});

onUnmounted(() => {
	if (observer) {
		observer.disconnect();
	}
});



// --- 暴露给父组件的方法 ---

const getScrollPosition = (): number => {
  return scrollContainerRef.value?.scrollTop || 0;
};


const setScrollPosition = (position: number) => {
  if (scrollContainerRef.value) {
    scrollContainerRef.value.scrollTop = position;
  }
};
const scrollToTop = () => {
	scrollContainerRef.value?.scrollTo({ top: 0, behavior: "smooth" });
};

defineExpose({
	scrollToTop,
	getScrollPosition,
	setScrollPosition,
});
</script>
